package net.thermetics.fx.auth;

import java.util.EnumSet;
import java.util.Set;
import java.util.UUID;

import net.thermetics.fx.auth.GaeUser.AppRole;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import com.google.appengine.api.users.User;


public class GoogleAccountsAuthenticationProvider implements AuthenticationProvider {
    private UserRegistry userRegistry;

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    	if (authentication instanceof UsernamePasswordAuthenticationToken) {
    		return basicAuthenticate(authentication);
    	}
    	else {
    		return gaeAuthenticate(authentication);
    	}
    }

    public final boolean supports(Class<?> authentication) {
        return (PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication)
        		|| UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

    public void setUserRegistry(UserRegistry userRegistry) {
        this.userRegistry = userRegistry;
    }
    
    private Authentication basicAuthenticate(Authentication authentication) {
    	String username = (String)authentication.getPrincipal();
    	String apiKey = (String)authentication.getCredentials();
    	
    	//TODO: use api key.
    	GaeUser user = userRegistry.findUser(username);

        if (user == null) {
            // User not in registry. Needs to register
        	throw new DisabledException("Non-registered user");
        }

        if (!user.isEnabled()) {
            throw new DisabledException("Account is disabled");
        }

        GaeUserAuthentication auth =  new GaeUserAuthentication(user, authentication.getDetails());
        auth.setAuthenticated(user.getApiKey().equals(apiKey)); //user must provide correct API key.
        return auth;
    }
    
    private Authentication gaeAuthenticate(Authentication authentication) {
        User googleUser = (User) authentication.getPrincipal();

        GaeUser user = userRegistry.findUser(googleUser.getUserId());

        if (user == null) {
            // User not in registry. Needs to register
        	UUID apiKeyUUID = UUID.randomUUID();
        	String apiKey = apiKeyUUID.toString().replace("-", "");
            user = new GaeUser(googleUser.getUserId(), googleUser.getNickname(), googleUser.getEmail(), apiKey);
            
            if (user.getEmail().endsWith("@example.com")) {
            	Set<AppRole> authorities = EnumSet.of(AppRole.USER, AppRole.STAFF);
            	user.setAuthorities(authorities);
            }
            else {
            	Set<AppRole> authorities = EnumSet.of(AppRole.USER);
            	user.setAuthorities(authorities);
            }
            
            //example.com kludge to work around staff being set to false when registering.
            if (user.getEmail().endsWith("@example.com")) {
            	user.setStaff(true);
            }
            else {
            	user.setStaff(false);
            }
            userRegistry.persistUser(user);
        }

        if (!user.isEnabled()) {
            throw new DisabledException("Account is disabled");
        }

        GaeUserAuthentication auth =  new GaeUserAuthentication(user, authentication.getDetails());
        auth.setAuthenticated(true); //most definitely authenticated from GAE.
        return auth;
    }
}
